/* QtLua -- Lua bindings for Qt
   Copyright (C) 2011, Jarek Pelczar

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA

*/

#ifndef QLUASCRIPTENGINE_H
#define QLUASCRIPTENGINE_H

#include <QObject>
#include <QHash>
#include <QStringList>
#include <QVariant>
#include "qluascriptvalue.h"

class QLuaScriptEnginePrivate;
class QLuaScriptValue;
class QLuaScriptContext;
class QLuaScriptProgram;
class QLuaScriptEngineAgent;
class QLuaException;

class QLuaScriptEngine : public QObject
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(QLuaScriptEngine)
public:
    enum ValueOwnership {
        QtOwnership,
        ScriptOwnership,
        AutoOwnership
    };

    typedef QLuaScriptValue (*FunctionSignature)(QLuaScriptContext *, QLuaScriptEngine *);

    explicit QLuaScriptEngine(QObject *parent = 0);
    ~QLuaScriptEngine();

    QLuaScriptValue newTable();
    QLuaScriptValue newMetaObject(const QMetaObject * mo);
    QLuaScriptValue newQObject(QObject * object, ValueOwnership ownership = QtOwnership);
    QLuaScriptValue newFromVariant(const QVariant&);
    QLuaScriptValue newWeakTable();
    QLuaScriptValue newUserData(int sizeOfObject);
    QLuaScriptValue newLightUserData(void * ptr);
    QLuaScriptValue newUserDataWithGc(int metaTypeId,
                                      int sizeOfT,
                                      void (* ctor_helper)(void *),
                                      void (* dtor_helper)(void *),
                                      const QLuaScriptValue& proto);

    void registerUserData(void * ptr, int metaType);
    void unregisterUserData(void * ptr, int metaType);

    //! Create method called with "self" object
    QLuaScriptValue newMethod(FunctionSignature signature);

    //! Create plain callable function. The "self" object will be the global object
    QLuaScriptValue newFunction(FunctionSignature signature);

    QByteArray compileCodeChunk(const QByteArray& code, const QByteArray& fileName, bool stripDebugInfo = false);

    QLuaScriptValue getGlobal(const QByteArray& name) const;
    void setGlobal(const QByteArray& key, const QLuaScriptValue& value);

    void executeProgram(const QByteArray& code, const QByteArray& fileName);
    void executeFile(const QString& path);

    void executeProgram(const QLuaScriptProgram& program);

    QLuaScriptEngineAgent * agent() const;
    void setAgent(QLuaScriptEngineAgent * agent);

    QHash<qint64, QLuaScriptProgram> loadedScripts() const;
    QLuaScriptContext * currentContext() const;
    QLuaScriptContext * rootContext() const;

    void collectGarbage();

    // Exception interface

    bool hasUncaughtException() const;
    QLuaException uncaughtException() const;
    void clearException();
    QLuaException newException(const QString& what);
    QLuaException newException(const QLuaScriptValue& what);

    //! This function will throw exception. It never returns.
    void throwException(const QLuaException& ex);

    static QStringList pluginImportPaths();
    static void setPluginImportPaths(const QStringList& paths);
    static void addPluginImportPath(const QString& path);

private:
    template<typename T> class udata_ctor_helper
    {
    public:
        static void ctor(void * p) {
            new(p) T();
        }

        static void dtor(void * p) {
            ((T *)p)->~T();
        }
    };

public:
    template<typename T> inline QLuaScriptValue newUserDataWithGc(const QLuaScriptValue& proto = QLuaScriptValue())
    {
        int meta_id = qMetaTypeId<T>();

        return newUserDataWithGc(meta_id,
                                 sizeof(T),
                                 &udata_ctor_helper<T>::ctor,
                                 &udata_ctor_helper<T>::dtor,
                                 proto);
    }
private:
    QLuaScriptEnginePrivate * d_ptr;
    Q_PRIVATE_SLOT(d_func(), void _q_objectDestroyed(QObject *))
};

#endif // QLUASCRIPTENGINE_H
